/**
 * Copyright 1996 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 *
 * Note, this is a very old runtime library that I have evolved since 1996. I rarely use it
 * now because the Apache Commons project has made it obsolete.
 */
package self.awt.painter;

import java.awt.*;
import java.awt.geom.*;
import java.awt.font.*;
import java.util.*;
import self.awt.*;

public class TextPainter {
//  private  TextPainter() {}
//  final static public TextPainter instance = new TextPainter();

  private static final FontRenderContext FRC = new FontRenderContext(new AffineTransform(), false, false);

  private ArrayList   lines = new ArrayList();
  private ArrayList   glyphVectors = new ArrayList();
  private ArrayList   lineXandWidthInfo = new ArrayList();
  private float[]     positions;
  private int         nextLineKind;
  private Font        font;
  private FontMetrics metrics;
  private Rectangle   rect = new Rectangle();
  private boolean     isCentered;
  private String      terminator;
  private int         termLen;
  private boolean     wroteAll;


  public int paint( Graphics g, ITextRenderInfo txt, boolean outlineOnly ) {
    rect.setBounds( txt.getBounds() );
    // bounds bug if the width/height is <9 then a stackoverflow is encountered -- asw 15/10/04
    if ( rect.width < 9 || rect.height < 9 )
      return rect.y;

    if ( outlineOnly ) {
      g.drawRect( rect.x, rect.y, rect.width, rect.height );
      return rect.y;
    }
    initVariables( g, txt );
    glyphVectors.clear();
    lines.clear();
    lineXandWidthInfo.clear();
    String output = txt.getText();

    if ( output == null )
      output = "";

    positions = new float[ output.length()*2 ];
    wroteAll = false;
    createGlyphVector( output );
    Color orig = g.getColor();
    g.setColor( txt.getColor() );
    int nextY = paintText( (Graphics2D)g, txt );
    g.setColor( orig );
    return nextY;
  }

  public boolean didWriteAll() {
    return wroteAll;
  }

  private int paintText( Graphics2D graphics2d, ITextRenderInfo txt ) {
    int max = glyphVectors.size();

    if ( max > 0 ) {
      boolean isUnderline = txt.isUnderlined();
      boolean isStrikeThrough = txt.isStrikeThrough();
      float ascent = metrics.getAscent();
      float descent = metrics.getDescent();
      float leading = metrics.getLeading();
      float height  = ascent + descent + leading;
      float strikeOffset = (ascent/2) - 2;

      float y = (float)rect.y + ascent;
      float bottom =(float)rect.y + (float)rect.height;
      for ( int cntr = 0; cntr < max && y < bottom; cntr++ ) {
        GlyphVector glyphvector = (GlyphVector)glyphVectors.get( cntr );
        if ( glyphvector != null ) {
          int[] xAndWidth = (int[]) lineXandWidthInfo.get(cntr );
          graphics2d.drawString( (String)lines.get(cntr), xAndWidth[0], y );
//          graphics2d.drawGlyphVector( glyphvector, xAndWidth[0], y );
          if ( isStrikeThrough )
            graphics2d.drawLine( xAndWidth[0], (int)(y-strikeOffset), xAndWidth[0]+xAndWidth[1], (int)(y-strikeOffset));
          if ( isUnderline )
            graphics2d.drawLine( xAndWidth[0], (int)(y+2), xAndWidth[0]+xAndWidth[1], (int)(y+2));
        }
        y += height;
        if ( cntr == max-1 )
          wroteAll = true;
      }
      return (int) (y - ascent - leading);
    }
    return rect.y;
  }

  private void initVariables( Graphics g, ITextRenderInfo txt ) {
    rect.grow( -1, -1 );
    font = txt.getFont();
    if ( (font != null) && (!font.equals(g.getFont())) )
      g.setFont( font );
    metrics = g.getFontMetrics( font );
    if ( txt.isWrapped() )
      nextLineKind = ITextRenderInfo.WRAPPED_FORMAT;
    else if ( txt.isTrimmed() )
      nextLineKind = ITextRenderInfo.TRIM_FORMAT;
    else
      nextLineKind = ITextRenderInfo.CUT_FORMAT;
    isCentered = txt.isCentered();
    termLen = 0;
    if ( txt.isTrimmed() ) {
      terminator = txt.getTrimmedTerminator();
      if ( terminator != null )
        termLen = terminator.length();
    }
  }

  private void createGlyphVector( String subdata ) {
    if ( handleNewLine(subdata) )
      return;
    GlyphVector lineAsGV = font.createGlyphVector( FRC, subdata );
    int glyphs = lineAsGV.getNumGlyphs();
    if ( glyphs == 0 )
      return;
    lineAsGV.getGlyphPositions( 0, glyphs, positions );
    int last = getTrimPositionIfAny( glyphs );
    if ( last > 0 ) {
      String line = subdata.substring( 0, last - 1 );
      int lineLen = line.length();
      switch ( nextLineKind ) {
        case ITextRenderInfo.WRAPPED_FORMAT:
          handleWrap( subdata, line, lineLen );
          break;
        case ITextRenderInfo.CUT_FORMAT:
          handleCut( line );
          break;
        case ITextRenderInfo.TRIM_FORMAT:
          handleTrim( line, lineLen );
          break;
      }
    } else {
      addToGlyphsVector( subdata, lineAsGV );
    }
  }

  private void handleTrim( String line, int lineLen ) {
    int stopAt = lineLen - (3+termLen);
    String term = "...";
    if ( terminator != null )
      term += terminator;
    String newLine = line.substring( 0, stopAt ) + term;
    GlyphVector lineAsGV = font.createGlyphVector( FRC, newLine );
    addToGlyphsVector( line, lineAsGV );
  }

  private void handleCut( String line ) {
    GlyphVector lineAsGV = font.createGlyphVector( FRC, line );
    addToGlyphsVector( line, lineAsGV );
  }

  private void handleWrap( String data, String line, int lineLen ) {
    int pos = getLastPunctuationCharIfAnyOnLine( line, lineLen );
    String newLine = data.substring( 0, pos );
    GlyphVector lineAsGV = font.createGlyphVector( FRC, newLine );
    addToGlyphsVector( newLine, lineAsGV );
    data = data.substring( pos );
    createGlyphVector( data );
  }

  private void addToGlyphsVector( String line, GlyphVector lineAsGV ) {
    if ( line != null ) {
      int width = metrics.stringWidth( line );
      int xVal =  isCentered ? rect.x + ((rect.width - width)/2): rect.x;
      lineXandWidthInfo.add( new int[] {xVal, width} );
    } else
      lineXandWidthInfo.add( null );
    glyphVectors.add( lineAsGV );
    lines.add( line );
  }

  private boolean handleNewLine( String subdata ) {
    int newLinePos = subdata.indexOf( '\n' );
    if ( newLinePos >= 0 ) {
      if ( newLinePos == 0 )
        addToGlyphsVector( null, null );
      else {
        String partA = subdata.substring( 0, newLinePos );
        createGlyphVector( partA );
      }
      String partB = subdata.substring( newLinePos+1 );
      createGlyphVector( partB );
      return true;
    }
    return false;
  }

  private int getTrimPositionIfAny( int glyphs ) {
    for ( int pos = 0; pos < glyphs; pos++ ) {
      if ( positions[pos*2] > rect.width )
        return pos;
    }
    return -1;
  }

  private int getLastPunctuationCharIfAnyOnLine( String line, int len ) {
    int cntr;
    for ( cntr = len-1; cntr >= 0; cntr-- ) {
      if ( !Character.isLetterOrDigit(line.charAt(cntr)) ) {
        cntr++;
        break;
      }
    }
    if ( cntr <= 0 )
      cntr = len;
    return cntr;
  }
}